package com.xu.cloud.common.util;

import com.xu.cloud.common.api.ApiContext;
import com.xu.cloud.common.constant.HttpConstant;
import com.xu.cloud.common.constant.LogConstant;
import com.xu.cloud.common.wrapper.XuCloudRequestWrapper;
import com.xu.cloud.common.wrapper.XuCloudResponseWrapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;

/**
 * Servlet日志记录工具类
 *
 * @author xuguan
 * @since 2024/1/10
 */
@Slf4j
public class ServletLogUtil {

    public static final Logger REQUEST_PREVIEW_LOGGER = LoggerFactory.getLogger("requestPreviewLogger");

    public static void requestLog(HttpServletRequest request) {
        log.info(">>>>>>>> HttpServlet请求开始 <<<<<<<<");

        String method = request.getMethod().toUpperCase();
        String protocol = request.getProtocol().substring(0, request.getProtocol().indexOf("/")).toLowerCase();
        String requestURI = request.getRequestURI();
        String queryString = request.getQueryString();

        StringBuilder sb = new StringBuilder();
        sb.append(protocol)
                .append("://")
                .append(request.getServerName())
                .append(":")
                .append(request.getServerPort())
                .append(requestURI);
        if (StringUtils.hasText(queryString)) {
            sb.append("?")
                    .append(queryString);
        }

        log.info("客户端IP={}", IpUtil.getClientIp(request));
        log.info("请求方法method={}, 请求路径url={}", method, sb.toString());
        log.info("请求头HttpHeader: {}", HttpServletUtil.getRequestHeaders());
        final Map<String, String> requestParams = HttpServletUtil.getRequestParams();
        if (!CollectionUtils.isEmpty(requestParams)) {
            log.info("请求参数requestParams: {}", requestParams);
        }
    }

    public static void requestBodyLog(ContentCachingRequestWrapper request) {
        final String requestBody = getServletContent(request.getContentAsByteArray(), StandardCharsets.UTF_8);
        log.info("请求体: {}", requestBody);
    }

    public static void requestBodyLog(XuCloudRequestWrapper request) {
        final String requestBody = getServletContent(request.getRequestBody(), StandardCharsets.UTF_8);
        log.info("请求体: {}", requestBody);
    }

    public static void responseLog(HttpServletResponse response) {
        log.info(">>>>>>>> HttpServlet请求完成 <<<<<<<<");
        int statusCode = response.getStatus();
        log.info("响应状态码: {}", statusCode);
        log.info("响应头HttpHeader: {}", HttpServletUtil.getResponseHeaders());
    }

    public static void resultLog(HttpServletRequest request) {
        final Object attribute = request.getAttribute(HttpConstant.XU_CLOUD_RESPONSE_ATTRIBUTE_NAME);
        if (Objects.nonNull(attribute)) {
            log.info("响应体: {}", JsonUtil.toJsonString(attribute));
            request.removeAttribute(HttpConstant.XU_CLOUD_RESPONSE_ATTRIBUTE_NAME);
        }
    }

    public static void responseBodyLog(ContentCachingResponseWrapper response) {
        String responseBody = getServletContent(response.getContentAsByteArray(), StandardCharsets.UTF_8);
        try {
            // 必须将响应流拷贝到response, 否则响应体为空
            response.copyBodyToResponse();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        log.info("响应体: {}", responseBody);
    }

    public static void responseBodyLog(XuCloudResponseWrapper response) {
        String responseBody = getServletContent(response.getResponseBody(), StandardCharsets.UTF_8);
        response.copyToResponse();
        log.info("响应体: {}", responseBody);
    }

    public static String getServletContent(byte[] bytes, Charset charset) {
        String content = new String(bytes, charset);
        if (StringUtils.hasText(content)) {
            content = content.replaceAll("\\s", "");
            return content;
        }
        return "{}";
    }

    public static void reqJournalTraceLog() {
        // 打印请求流水号和traceId
        log.info("请求流水号reqJournalNo={}, 追踪号traceId={}", MDC.get(LogConstant.REQUEST_JOURNAL_NO),
                MDC.get(LogConstant.TRACE_ID));
    }

    public static void requestPreviewLog() {
        ApiContext apiContext = ApiContext.current();
        String reqTime = LocalDateTimeUtil.format(apiContext.getReqTime(), LocalDateTimeUtil.NORM_DATE_TIME_MS_PATTERN);
        String respTime = LocalDateTimeUtil.format(apiContext.getRespTime(), LocalDateTimeUtil.NORM_DATE_TIME_MS_PATTERN);
        final String throwableType = Objects.isNull(apiContext.getThrowable())
                ? ""
                : apiContext.getThrowable().getClass().getSimpleName();
        String costMillis = apiContext.getTransCostMillis() + "ms";

        REQUEST_PREVIEW_LOGGER.info("{}|{}|{}|{}|{}|{}|{}|{}|{}",
                apiContext.getApiCode(),
                apiContext.getApiPath(),
                apiContext.getClientIp(),
                reqTime,
                respTime,
                throwableType,
                apiContext.getCode(),
                apiContext.getMsg(),
                costMillis);
    }
}
